#include "Entity.h"

Entity::Entity()
{
	/**
	Initialize the members of the Entity class
	*/
	D3DXMatrixIdentity(&cameraMatrix);
	x = y = z = 0.0f;
	yaw = pitch = roll = 0.0f;
	sx = sy = sz = 1.0f;
	mass = 1.0f;
	friction = 0.5f;
	restitution = 0.0f;
	phys_body = NULL;
	physType = NONPHYS;
	texture = NULL;
	this->layer = 0.0f;

}

Entity::~Entity()
{
	/**
	Nothing to delete yet!
	*/
}


void Entity::init(LPDIRECT3DDEVICE9 d3ddev, b2World *world_physics)
{
	/**
	Set the world_physics ptr to that of the handed down world_physics usually initialized
	and memory allocated in Resource
	*/
	this->world_physics = world_physics;
}


void Entity::setLayer(float layer)
{
	this->layer = layer;
}

float Entity::GetLayer()
{
	return layer;
}

void Entity::setPosition(float x, float y)
{
	/**
	Set position for the object. If the object is of a dynamic type the setPosition
	acts more like a means to put a force on the entity.

	Lets us set the xyz values where z will be used for our zoom.
	*/

	this->x = x;
	this->y = y;

	bodydef.position.Set(x,y);

	if (phys_body != NULL)
	{
		phys_body->SetXForm( b2Vec2(x, y), roll);
	}
}

void Entity::setPosition(float x, float y, float z)
{
	/**
	Set position for the object. If the object is of a dynamic type the setPosition
	acts more like a means to put a force on the entity.

	Lets us set the xyz values where z will be used for our zoom.
	*/

	this->x = x;
	this->y = y;
	this->z = z;
	bodydef.position.Set(x,y);

	if (phys_body != NULL)
	{
		phys_body->SetXForm( b2Vec2(x, y), roll);
	}
}


float Entity::GetXPosition()
{
	/**
	GetPosition of the object.
	*/

	if( this->physType == DYNAMIC )
		return phys_body->GetPosition().x;

	return this->x;
}

float Entity::GetYPosition()
{
	/**
	GetPosition of the object.
	*/
	if( this->physType == DYNAMIC )
		return phys_body->GetPosition().y;

	return this->y;
}

float Entity::GetZPosition()
{
	/**
	GetPosition of the object.
	*/

	return this->z;
}

float Entity::GetScaleX()
{
	return this->sx;
}

float Entity::GetScaleY()
{
	return this->sy;
}

void Entity::setScale(float sx, float sy, float sz)
{
	/**
	Set the size of the object.
	*/
	this->sx = sx; 
	this->sy = sy; 
	this->sz = sz;

	if( phys_body != NULL )
	{
		/**
		If the shape exist remove the shape and re-create it.
		*/
		
		if( shapeType == CIRCLE )
		{
			phys_body->DestroyShape( phys_body->GetShapeList() );

			b2CircleDef c;
			c.density = 1.0f;
			c.friction = friction;
			c.radius = sx*4.0f;
			c.restitution = restitution;
			phys_body->CreateShape(&c);
			phys_body->SetMassFromShapes();
		}
	}
}

void Entity::setRotationYaw(float yaw)
{
	/**
	Set the rotation of the object. You are rotating along yz.
	*/
	this->yaw = yaw;
}

void Entity::setRotationPitch(float pitch)
{
	/**
	Set the rotation of the object. You are rotating along xz.
	*/
	this->pitch = pitch;
}

void Entity::setRotationRoll(float roll)
{
	/**
	Set the rotation of the object. You are rotating along xy.
	*/

	this->roll = roll;
	bodydef.angle = roll;

	//If phys_body points to no data then it fails. If phys_body does find the data
	//then we check to see wither it's static or dynamic.
	//If it's dynamic then we put a torque( rotational ) force on it. 
	//If static then we reposition it.

	if (phys_body != NULL)
	{
		if ( physType == DYNAMIC )
		{
			phys_body->ApplyTorque(roll*(3.14f/180.0f));
			this->roll = phys_body->GetAngle();
		}
		else if( physType == STATIC )
		{
			phys_body->SetXForm( b2Vec2(this->x, this->y), roll*(3.14f/180.0f));
		}
	}
	
}

void Entity::setCamera(Camera *camera)
{
	D3DXMatrixTranslation(&cameraMatrix, camera->GetXPosition(), camera->GetYPosition(), camera->GetZPosition());
}

void Entity::addForce(float x, float y)
{
	if ( physType == DYNAMIC )
	{
		phys_body->ApplyForce( b2Vec2(x, y), b2Vec2(phys_body->GetPosition()) );
			this->x = 0.0f;
			this->y = 0.0f;
	}
}

void Entity::addRotForce(float rotationForce)
{
	if ( physType == DYNAMIC )
	{
		phys_body->ApplyTorque(-rotationForce*(3.14f/180.0f));
		this->roll = phys_body->GetAngle();
	}
}

void Entity::setMass(float mass)
{
	/**
	We set the mass for the entity. If static we set it to 0.0f because Box2D
	knows objects are static if the mass is 0.
	*/

	if (physType == STATIC)
		this->mass = 0.0f;
	else
	{
		if( physType == DYNAMIC && mass < 0.0f )
			this->mass = 1.0f;
		else
			this->mass = mass;

		if( phys_body != NULL )
		{
			b2MassData md;
			md.center = phys_body->GetLocalCenter();
			md.I = phys_body->GetInertia();
			md.mass = mass;
			phys_body->SetMass(&md);
			phys_body->SetMassFromShapes();
		}
	}
}

void Entity::setFriction(float friction)
{
	/**
	Sets the friction for the object
	*/
	this->friction = friction;

	if( phys_body != NULL )
	{
		if( shapeType == BOX)
		{
			phys_body->DestroyShape( phys_body->GetShapeList() );

			b2PolygonDef shapedef;
			shapedef.SetAsBox(sx, sy);
			shapedef.density = 1.0f;
			shapedef.friction = friction;
			shapedef.restitution = restitution;

			phys_body->CreateShape(&shapedef);
			phys_body->SetMassFromShapes();
		}
		else if( shapeType == CIRCLE)
		{
			phys_body->DestroyShape( phys_body->GetShapeList() );

			b2CircleDef c;
			c.density = 1.0f;
			c.friction = friction;
			c.radius = sx*4.0f;
			c.restitution = restitution;
			
			phys_body->CreateShape(&c);
			phys_body->SetMassFromShapes();
		}
	}
}

void Entity::setElastic(float restitution)
{
	/**
	Sets the elasticity of an object
	*/
	this->restitution = restitution;

	if( phys_body != NULL )
	{
		if( shapeType == BOX)
		{
			phys_body->DestroyShape( phys_body->GetShapeList() );

			b2PolygonDef shapedef;
			shapedef.SetAsBox(sx, sy);
			shapedef.density = 1.0f;
			shapedef.friction = friction;
			shapedef.restitution = restitution;

			phys_body->CreateShape(&shapedef);
			phys_body->SetMassFromShapes();
		}
		else if( shapeType == CIRCLE)
		{
			phys_body->DestroyShape( phys_body->GetShapeList() );

			b2CircleDef c;
			c.density = 1.0f;
			c.friction = friction;
			c.radius = sx*4.0f;
			c.restitution = restitution;
			
			phys_body->CreateShape(&c);
			phys_body->SetMassFromShapes();
		}

	}

}

void Entity::ConnectBody(Entity * entity, int type)
{
	jointdef.Initialize(this->phys_body, entity->phys_body, b2Vec2(x, y), entity->phys_body->GetPosition() );
	jointdef.length = 3.5f;
	jointdef.collideConnected = false;


	djoint = world_physics->CreateJoint(&jointdef);
}


void Entity::createPhysics(int shapeType, int physType, int numOfPoints, const PolyPos * pointPositions)
{
	/**
	This turns the object into an actual physical body or rigid body. The objects attributes
	should be set before making this call ie it's mass, friction, elasticity etc.

	It's switch case that lets you choose between 3 types of physical forms. A box, cirlcle, or
	polygon (Where the vertices are specified or something like it (the most accurate).
	*/

	/**
	Set the objects physical body type.
	NONPHYS (default) [ see ObjectTypes.h ]
	STATIC
	DYNAMIC
	*/

	this->shapeType = shapeType;
	this->physType = physType;


	if ( physType == STATIC || physType == DYNAMIC)
	{

		switch(shapeType)
		{
			case BOX:
			{
				bodydef.position.Set(x,y);
				bodydef.massData.center = b2Vec2(0.0f, 0.0f);
				bodydef.massData.I = 0.0f;

				if ( physType == STATIC )
					bodydef.massData.mass = 0.0f;
				else
					bodydef.massData.mass = mass;

				bodydef.allowSleep = true;

				phys_body = world_physics->CreateBody(&bodydef);

				b2PolygonDef shapedef;
				shapedef.SetAsBox(sx, sy);
				shapedef.density = 1.0f;
				shapedef.friction = friction;
				shapedef.restitution = restitution;

				phys_body->CreateShape(&shapedef);
				
				if ( physType == DYNAMIC )
					phys_body->SetMassFromShapes();
			}
			break;
			
			case CIRCLE:
			{
				bodydef.position.Set(x,y);
				//bodydef.massData.center = b2Vec2(0.0f, 0.0f);
				//bodydef.massData.I = 0.0f;

				if ( physType == STATIC )
					bodydef.massData.mass = 0.0f;
				else
					bodydef.massData.mass = mass;

				bodydef.allowSleep = true;

				phys_body = world_physics->CreateBody(&bodydef);

				b2CircleDef c;
				c.density = 1.0f;
				c.friction = friction;
				c.radius = sx*4.0f;
				
				c.restitution = restitution;
				

				phys_body->CreateShape(&c);

				if ( physType == DYNAMIC )
					phys_body->SetMassFromShapes();
			
			}
			break;
			
			case POLY:
			{
				bodydef.position.Set(x,y);

				if ( physType == STATIC )
					bodydef.massData.mass = 0.0f;
				else
					bodydef.massData.mass = mass;

				bodydef.allowSleep = true;

				phys_body = world_physics->CreateBody(&bodydef);

				
				pp = new b2PolygonDef();
				
				pp->vertexCount=numOfPoints;

				float avgVX=0.0f;
				float avgVY=0.0f;

				for(int i=0; i<numOfPoints; i++)
				{
					avgVX += pointPositions[i].x;
					avgVY += pointPositions[i].y;
				}


				if( pointPositions[0].x <= pointPositions[1].x)
				{
					if(pointPositions[0].y > pointPositions[1].y)
					{
						for(int i=numOfPoints-1; i>=0; i--)
						{
							pp->vertices[i].Set( -(pointPositions[i].x-(avgVX/numOfPoints))/30.0f, (pointPositions[i].y-(avgVY/numOfPoints))/30.0f);
							_cprintf("1.Position X Y:%f, %f\n", -(pointPositions[i].x-(avgVX/numOfPoints))/30.0f, -(pointPositions[i].y-(avgVY/numOfPoints))/30.0f);
						}
					}
					else if(pointPositions[0].y <= pointPositions[1].y)
					{
						for(int i=numOfPoints-1; i>=0; i--)
						{
							pp->vertices[i].Set( -(pointPositions[i].x-(avgVX/numOfPoints))/30.0f, -(pointPositions[i].y-(avgVY/numOfPoints))/30.0f);
							_cprintf("2.Position X Y:%f, %f\n", -(pointPositions[i].x-(avgVX/numOfPoints))/30.0f, -(pointPositions[i].y-(avgVY/numOfPoints))/30.0f);
						}
					}
				}
				else
				{
					if(pointPositions[0].y >= pointPositions[1].y)
					{
						for(int i=0; i<numOfPoints; i++)
						{
							_cprintf("3.Position X Y:%f, %f\n", (pointPositions[i].x-(avgVX/numOfPoints))/30.0f, -(pointPositions[i].y-(avgVY/numOfPoints))/30.0f);
							pp->vertices[i].Set( -(pointPositions[i].x-(avgVX/numOfPoints))/30.0f, (pointPositions[i].y-(avgVY/numOfPoints))/30.0f);
						}
					}
					else
					{
						for(int i=0; i<numOfPoints; i++)
						{
							_cprintf("4.Position X Y:%f, %f\n", -(pointPositions[i].x-(avgVX/numOfPoints))/30.0f, (pointPositions[i].y-(avgVY/numOfPoints))/30.0f);
							pp->vertices[i].Set( -(pointPositions[i].x-(avgVX/numOfPoints))/30.0f, (pointPositions[i].y-(avgVY/numOfPoints))/30.0f);
						}
					}
					
				}

				

				//pp.vertexCount=4;
				//pp.vertices[0].Set(-1.0f, -2.0f);
				//pp.vertices[1].Set(1.0f, -2.0f);
				//pp.vertices[2].Set(1.0f, 2.0f);
				//pp.vertices[3].Set(-1.0f, 2.0f);

				pp->density = 1.0f;
				pp->friction=friction;
				pp->restitution=restitution;

				phys_body->CreateShape( pp );

				if ( physType == DYNAMIC )
					phys_body->SetMassFromShapes();
			}
			break;

			default:
				this->createPhysics(BOX, NONPHYS);
			break;
		}
	}
	else
	{
	}
}

void Entity::SetTexture(Texture *texture)
{
	/**
	Sets the texture to be used for the object
	*/
	this->texture = texture;
}

Texture * Entity::GetTexture()
{
	return texture;
}

void Entity::Render(LPDIRECT3DDEVICE9 d3ddev)
{
	/**
	Render an object (left blank since entity itself shouldn't be renderable but's children
	like Sprite should overload this function to have something to draw).
	*/
}